// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

// @author: woodfan
// @function: FIR basic function module, to 实现乘加
// @data: 2020 year 3 month 19 day 14:02:33

/************************************************/
/*
This module can implemented in just a DSP48E1 slice. 
DSP48E1 is aviliable in 7 series and Virtex 6 .

The founctin used in DSP48E1 is disply below:
1. pre-add
2. multiply
3. mega register (not used in fact. Even though I want to use mega in the DSP slice, the Synthesizer seem not to use it. )
*/
/************************************************/
module fir_base
#(
	parameter DATA_BITS = 16,
	parameter COEF_BITS = 16,
	parameter EXTEND_BITS = 5,
	parameter OUT_BITS = DATA_BITS + COEF_BITS + 1
	// parameter OUT_BITS = DATA_BITS + COEF_BITS + EXTEND_BITS
)
(
	// System signal
	input							clk			,
	input							rst			,
	// Control and data input signal
	input							en			,
	input	signed [DATA_BITS-1:0]	data_in_A	,
	input	signed [DATA_BITS-1:0]	data_in_B	,
	input	signed [COEF_BITS-1:0]	coef		,
	// Output signal 
	output	reg						fir_busy	,	//no use
	output	signed 	[OUT_BITS-1:0]	data_out	,
	output	wire					output_vld	
);
// 乘法器结果位宽扩展为两个信号位宽之和
// https://zhuanlan.zhihu.com/p/92828553

/*
// 因为FIR系数是中心对称的，所以直接把中心对称的数据相加乘以系数
// 相加符号位扩展一位
wire signed [DATA_BITS:0]	data_in ;
assign data_in = {data_in_A[DATA_BITS-1], data_in_A} + {data_in_B[DATA_BITS-1], data_in_B};	//1. pre-add

// assign data_out =  'h1; 		//only use in Test model
assign data_out =  data_mult;	//There is need to extend sign bit, because the signals have been defined as signed signals.
 */

reg signed [OUT_BITS-1 :0]	data_mult;
// assign data_mult = data_in * coef; // Here is 17 x 16 bits 
assign output_vld = 1'b1;
// assign data_out = coef != 0 ? data_mult : 0;

assign data_out = coef != 0 ? p[OUT_BITS-1:0] : 0;

/* 
// when en is high, output the multiplied answer.
// 2. m register
// This 
always @(posedge clk or posedge rst) begin
	if (rst) begin
		// reset
		fir_busy	<=	1'b0;
		data_mult	<= 	0 ;
		output_vld	<=	1'b0;
	end
	else if (en) begin
		//if coef is 0, the answer is directly 0 ;
		data_mult	<=	coef != 0 ? p[OUT_BITS-1:0] : 0;
		output_vld	<=	1'b1;
	end
	else begin
		data_mult	<=	'd0;
		output_vld	<=	1'b0;
	end
end */

/****************************************************************/
// uncomment the block when use dsp48_macro IP core
/* 
wire signed [31 :0]	data_mult;
xbip_dsp48_macro_0 U_dsp48e1 (
  .CLK(clk),  // input wire CLK
  .A(data_in_A),      // input wire [15 : 0] A
  .B(coef),      // input wire [15 : 0] B
  .D(data_in_B),      // input wire [15 : 0] D
  .P(data_mult)      // output wire [31 : 0] P
);
 */
/****************************************************************/


//Use DSP48E1 Primitive
wire signed [24:0]        d;
wire signed [24:0]        a;
wire signed   [17:0]        b;
// reg    [47:0]        c;
wire signed [47:0]        p;
assign a = data_in_A;
assign d = data_in_B;
assign b = coef;
// mode (5'b01100 ) : use function -> p = (a+b)*d + c
dsp48_core#(
    .S        (0     ),
    .USE_DPORT("TRUE"),
    .AREG     (1     ),
    .BREG     (2     )
) inst_dsp48_core(
    .clock(clk),
    .a    (a    ),
    .b    (b    ),
    .c    (48'h0    ),
    .d    (d    ),
    .mode (5'b01100 ),
    .p(p)
);

endmodule